package cn.turboinfo.fuyang.api.domain.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
public class BeanHelper {

    /**
     * 静默按属性路径获取对象属性, 获取不到不报错
     *
     * @param bean         获取来源对象
     * @param propertyPath 属性路径, 形如: a.b.c
     * @return
     */
    public static Object deepGetPropertySilently(Object bean, String propertyPath) {
        String[] paths = StringUtils.split(propertyPath, ".");
        Object result = bean;

        for (String path : paths) {
            try {
                result = PropertyUtils.getProperty(result, path);
            } catch (Exception e) {
                log.error("获取属性出错, propertyPath={}, path={}", propertyPath, path);
                log.error(e.getMessage(), e);
                // 出错以后不再继续执行
                break;
            }
        }

        return result;
    }

    /**
     * 复制属性到 lombok builder
     *
     * @param builder             builder 对象
     * @param propertiesTypeClass 根据该类型来确定复制哪些属性
     * @param source              从哪个对象复制
     * @param <Builder>           Builder 类型
     * @param <PropertiesType>    确定复制属性的类型
     * @param <PropertiesSource>  复制属性值的来源对象类型
     */
    public static <Builder, PropertiesType, PropertiesSource> void copyPropertiesToBuilder(Builder builder, Class<PropertiesType> propertiesTypeClass, PropertiesSource source) {
        PropertyDescriptor[] propertyDescriptors = PropertyUtils.getPropertyDescriptors(propertiesTypeClass);
        Map<String, List<Method>> methodNameListMap = Arrays.stream(builder.getClass().getMethods())
                .collect(Collectors.groupingBy(Method::getName));
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            String fieldName = propertyDescriptor.getName();
            try {
                Object fieldValue = PropertyUtils.getProperty(source, fieldName);
                if (fieldValue != null) {
                    // 反射调用 builder 的赋值方法
                    // 对于 lombok 生成的 builder 属性名和方法名一样
                    String methodName = fieldName;

                    if (methodNameListMap.containsKey(methodName)) {
                        Optional<Method> methodOptional = methodNameListMap.get(methodName).stream()
                                .filter(method -> method.getParameterCount() == 1)
                                .filter(method -> method.getParameterTypes()[0].isAssignableFrom(fieldValue.getClass()))
                                .findAny();
                        if (methodOptional.isPresent()) {
                            Method method = methodOptional.get();
                            method.invoke(builder, fieldValue);
                        }
                    }
                }
            } catch (IllegalAccessException | NoSuchMethodException e) {
                // did nothing
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                log.error("类属性拷贝错误, message={}, fieldName={}", e.getMessage(), fieldName);
            }
        }
    }

}
